use base64::Engine;
use itrustee_verifier::evidence::ITrusteeEvidence;
use serde_json::Value;
use std::fs;
use std::path::PathBuf;

fn read_test_input(filename: &str) -> Value {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("tests/data");
    path.push(filename);
    let content = fs::read_to_string(path).expect("Failed to read input file");
    serde_json::from_str(&content).expect("Failed to parse input JSON")
}

fn read_evidence_json() -> Value {
    read_test_input("report.json")
}

fn read_log_data_json() -> Value {
    read_test_input("log_data.json")
}

/// Helper function to get the inner JSON from log_data and modify it
fn modify_inner_json<F>(right_report: &mut Value, modifier: F)
where
    F: FnOnce(&mut Value),
{
    if let Some(logs_array) = right_report.get_mut("logs").and_then(|l| l.as_array_mut()) {
        // Get the first log entry from the array
        if let Some(first_log) = logs_array.get_mut(0) {
            if let Some(log_data_b64) = first_log.get("log_data").and_then(|ld| ld.as_str()) {
                // Decode the base64 to get the inner JSON
                let raw = base64::engine::general_purpose::STANDARD.decode(log_data_b64).unwrap();
                let mut inner_json: Value = serde_json::from_slice(&raw).unwrap();

                // Apply the modifier function
                modifier(&mut inner_json);

                // Re-encode and set back
                let new_inner_str = serde_json::to_string(&inner_json).unwrap();
                let new_log_data = base64::engine::general_purpose::STANDARD.encode(new_inner_str);

                if let Some(log_obj) = first_log.as_object_mut() {
                    log_obj.insert("log_data".to_string(), serde_json::json!(new_log_data));
                }
            }
        }
    }
}

/// correct evidence json
#[test]
fn test_itrustee_evidence_from_json_value() {
    let report: Value = read_evidence_json();
    let log_data: Value = read_log_data_json();

    let result = ITrusteeEvidence::from_json_value(&report);
    assert!(result.is_ok(), "Failed to parse ITrusteeEvidence: {:?}", result.err());
    let evidence = result.unwrap();
    assert_eq!(evidence.handler, log_data["handler"].as_str().unwrap())
}

/// Test missing evidence.log field
#[test]
fn test_itrustee_evidence_missing_log() {
    let mut right_report = read_evidence_json();

    // Remove log field
    right_report.as_object_mut().unwrap().remove("logs");

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test missing log.log_data field
#[test]
fn test_itrustee_evidence_missing_log_data() {
    let mut right_report = read_evidence_json();

    // Remove log_data field from the first log entry
    if let Some(logs_array) = right_report.get_mut("logs").and_then(|l| l.as_array_mut()) {
        if let Some(first_log) = logs_array.get_mut(0).and_then(|l| l.as_object_mut()) {
            first_log.remove("log_data");
        }
    }

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test log.log_data is not a string
#[test]
fn test_itrustee_evidence_log_data_not_string() {
    let mut right_report = read_evidence_json();

    // Change log_data to not a string in the first log entry
    if let Some(logs_array) = right_report.get_mut("logs").and_then(|l| l.as_array_mut()) {
        if let Some(first_log) = logs_array.get_mut(0).and_then(|l| l.as_object_mut()) {
            first_log.insert("log_data".to_string(), serde_json::json!(123));
        }
    }

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid base64 encoding in log_data
#[test]
fn test_itrustee_evidence_invalid_base64() {
    let mut right_report = read_evidence_json();

    // Set log_data to invalid base64 in the first log entry
    if let Some(logs_array) = right_report.get_mut("logs").and_then(|l| l.as_array_mut()) {
        if let Some(first_log) = logs_array.get_mut(0).and_then(|l| l.as_object_mut()) {
            first_log.insert("log_data".to_string(), serde_json::json!("invalid_base64!@#$%"));
        }
    }

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid JSON in decoded log_data
#[test]
fn test_itrustee_evidence_invalid_inner_json() {
    let mut right_report = read_evidence_json();

    // Set log_data to invalid JSON (but valid base64) in the first log entry
    let invalid_json_b64 = base64::engine::general_purpose::STANDARD.encode("invalid json content {");

    if let Some(logs_array) = right_report.get_mut("logs").and_then(|l| l.as_array_mut()) {
        if let Some(first_log) = logs_array.get_mut(0).and_then(|l| l.as_object_mut()) {
            first_log.insert("log_data".to_string(), serde_json::json!(invalid_json_b64));
        }
    }

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test missing handler field in inner JSON
#[test]
fn test_itrustee_evidence_missing_handler() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        inner_json.as_object_mut().unwrap().remove("handler");
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test missing payload field in inner JSON
#[test]
fn test_itrustee_evidence_missing_payload() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        inner_json.as_object_mut().unwrap().remove("payload");
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test missing report_sign field in inner JSON
#[test]
fn test_itrustee_evidence_missing_report_sign() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        inner_json.as_object_mut().unwrap().remove("report_sign");
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test missing akcert field in inner JSON
#[test]
fn test_itrustee_evidence_missing_akcert() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        inner_json.as_object_mut().unwrap().remove("akcert");
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid payload structure
#[test]
fn test_itrustee_evidence_invalid_payload() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        if let Some(payload_obj) = inner_json.get_mut("payload") {
            *payload_obj = serde_json::json!("invalid_payload_structure");
        }
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid handler structure
#[test]
fn test_itrustee_evidence_invalid_handler() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        if let Some(handler_obj) = inner_json.get_mut("handler") {
            *handler_obj = serde_json::json!(123); // handler should be string
        }
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid report_sign structure
#[test]
fn test_itrustee_evidence_invalid_report_sign() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        if let Some(report_sign_obj) = inner_json.get_mut("report_sign") {
            *report_sign_obj = serde_json::json!("invalid_report_sign_structure");
        }
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}

/// Test invalid akcert structure
#[test]
fn test_itrustee_evidence_invalid_akcert() {
    let mut right_report = read_evidence_json();

    modify_inner_json(&mut right_report, |inner_json| {
        if let Some(akcert_obj) = inner_json.get_mut("akcert") {
            *akcert_obj = serde_json::json!("invalid_akcert_structure");
        }
    });

    let result = ITrusteeEvidence::from_json_value(&right_report);
    assert!(result.is_err());
}
